#pragma once

#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>

#include <hash_map>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <Utilities\glext.h>

typedef std::map<std::string, GLint, std::less<std::string> > ShaderMap;

class ShaderResource
{
public:
	void Create(UINT_PTR shaderResourceID, std::string name);
	const std::string GetSource(void) const;
	const bool GetIsValid(void) const;
	virtual const std::string GetName(void) const = 0;
	const int GetType(void) const;

protected:
	std::string _source;
	bool _valid;
	std::string _name;
	int _type;
};

class VertexShaderResource : public ShaderResource
{
public:
	VertexShaderResource(UINT_PTR shaderResourceID, std::string name);
	const std::string GetName(void) const;
};

class FragmentShaderResource : public ShaderResource
{
public:
	FragmentShaderResource(UINT_PTR shaderResourceID, std::string name);
	const std::string GetName(void) const;
};

class Shader
{
public:
	Shader(void);
	Shader(const std::vector<ShaderResource*>& shaders);
	virtual ~Shader(void);

	void Create(const std::vector<ShaderResource*>& shaders);
	void SetUniform1i(const std::string name, const GLint value) const;
	void SetUniform1f(const std::string name, const GLfloat value) const;
	void SetUniform2fv(const std::string name, const GLfloat* const value) const;
	void SetUniform3fv(const std::string name, const GLfloat* const value) const;

	void Enable(void) const;
	void Enable(const bool enable) const;
	bool IsEnabled(void) const;
	const GLuint GetProgramId() const;
private:
	Shader(const Shader&);

	const bool BuildFiles(const std::vector<ShaderResource*>& shaders, std::string& log);
	const bool Compile(const std::vector<ShaderResource*>& shaders, std::string& log);
	const bool Link(std::string& log);
	const bool CompileShader(const ShaderResource* const shader, std::string& log);
	const std::string GetShaderInfoLog(const GLuint shader) const;
	const std::string GetProgramInfoLog(const GLuint program) const;

	void InitializeShaderParameters(void);
	const int GetUniformParameter(const std::string name) const;
	const int GetAttribParameter(const std::string name) const;
	const int GetParameter(const ShaderMap& map, const std::string name) const;

	GLuint _programObject;
	std::vector<GLuint> _shaderObjects;

	ShaderMap _uniformParameters, _attributeParameters;
};
